#include "RenderTarget.h"
#include "graphics/GLState.h"
#include <iostream>
using namespace Umikaze::Core;

RenderTarget::RenderTarget(
    int width, int height,
    const std::vector<ColorAttachmentInfo> &colorAttachmentInfos)
    : RenderTarget(width, height) {

  for (auto &info : colorAttachmentInfos) {
    _colorAttachments.insert(
        {info._location,
         std::make_shared<RenderTexture>(_width, _height, info._format)});
  }
}

RenderTarget::RenderTarget(
    int width, int height,
    const std::vector<ColorAttachmentInfo> &colorAttachmentInfos,
    DepthStencilFormat depthStencilFormat)
    : RenderTarget(width, height, colorAttachmentInfos) {
  _depthAttachment =
      std::make_shared<RenderTexture>(_width, _height, depthStencilFormat);
}

RenderTarget::RenderTarget(int width, int height,
                           const ColorAttachmentInfo &info,
                           DepthStencilFormat depthStencilFormat)
    : RenderTarget(width, height) {
  _colorAttachments.insert(
      {info._location,
       std::make_shared<RenderTexture>(_width, _height, info._format)});
  _depthAttachment =
      std::make_shared<RenderTexture>(_width, _height, depthStencilFormat);
}

RenderTarget::RenderTarget(const std::shared_ptr<RenderTexture> &targetTexture,
                           bool isDepth)
    : RenderTarget(targetTexture->_width, targetTexture->_height) {

  if (isDepth) {
    _depthAttachment = targetTexture;
  } else {
    _colorAttachments.insert({0, targetTexture});
  }
}

RenderTarget::RenderTarget(const std::shared_ptr<RenderTexture> &targetTexture,
                           const std::shared_ptr<RenderTexture> &depthTexture)
    : RenderTarget(targetTexture) {
  _depthAttachment = depthTexture;
}

void RenderTarget::setTargetColorTexture(
    const std::shared_ptr<RenderTexture> &colorTexture, uint32_t location) {
  if (colorTexture->_height != _height || colorTexture->_width != _width) {
    // TODO
    std::cerr << "  " << std::endl;
    return;
  }
  if (auto iter = _colorAttachments.find(location);
      iter != _colorAttachments.end()) {
    iter->second = colorTexture;
  } else {
    _colorAttachments.insert({location, colorTexture});
  }
  _colorNeedUpdate = true;
}

void RenderTarget::setTargetDepthTexture(
    const std::shared_ptr<RenderTexture> &depthTexture) {
  if (depthTexture->_height != _height || depthTexture->_width != _width) {
    std::cerr << "  " << std::endl;
    return;
  }
  _depthAttachment = depthTexture;
  _depthNeedUpdate = true;
}

void RenderTarget::addAttachment() {
  if (_colorNeedUpdate) {
    for (auto &iter : _colorAttachments) {
      iter.second->bindAsColorAttachment(iter.first);
    }
    _colorNeedUpdate = false;
  }

  if (_depthNeedUpdate && _depthAttachment) {
    _depthAttachment->bindAsDepthAttachment();
    _depthNeedUpdate = false;
  }

  if (auto code = glCheckFramebufferStatus(GL_FRAMEBUFFER);
      code != GL_FRAMEBUFFER_COMPLETE)
    std::cerr << "ERROR::FRAMEBUFFER:: Framebuffer is not complete! Code:"
              << std::hex << code << std::endl;
}

void RenderTarget::bind() {
  if (!_fbo) {
    glGenFramebuffers(1, &_fbo);
  }
  auto &state = GlState::instance();
  state->bindFramebuffer(_fbo);
  addAttachment();
  state->viewport(0, 0, _width, _height);
}

void RenderTarget::setSize(int width, int height) {
  _width = width;
  _height = height;
}
